#include "RenderWorld.h"
#include "RenderModelLocal.h"
#include "RenderModelManager.h"
#include "RenderSystemGLES.h"
#include "./OGL_ES_PVR/include/gles/gl.h"
#include "../framework/CVarSystem.h"

extern CVar rs_lightMap;

static void R_GLPerspectivef(float fovy, float aspect, float zNear, float zFar)
{
    float xmin, xmax, ymin, ymax;

    ymax = zNear * (float)inf::math::tan(fovy * inf::math::PI / 360.0f);
    ymin = -ymax;
    xmin = ymin * aspect;
    xmax = ymax * aspect;

    glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);
}

static inf::Mat4 R_BuildViewMatrixLookAt(const inf::Vec3& vEye, const inf::Vec3& vAt, const inf::Vec3& vUp, bool bRightHanded)
{
    inf::Vec3 vForward, vUpNorm, vSide;
    inf::Mat4 result;
    float*  mPtr = (float*)result.to_float_ptr();

    vForward = (bRightHanded) ? vEye - vAt : vAt - vEye;

    vForward.normalize();
    vUpNorm = vUp;
    vUpNorm.normalize();
    vSide   = vUpNorm.cross( vForward);
    vUpNorm = vForward.cross(vSide);

    mPtr[0]=vSide.x;	mPtr[4]=vSide.y;	mPtr[8]=vSide.z;		mPtr[12]=0;
    mPtr[1]=vUpNorm.x;	mPtr[5]=vUpNorm.y;	mPtr[9]=vUpNorm.z;		mPtr[13]=0;
    mPtr[2]=vForward.x; mPtr[6]=vForward.y;	mPtr[10]=vForward.z;	mPtr[14]=0;
    mPtr[3]=0;			mPtr[7]=0;			mPtr[11]=0;				mPtr[15]=1;

    mPtr[12] += (-vEye.x*mPtr[0])+(-vEye.y*mPtr[4])+(-vEye.z*mPtr[8]);
    mPtr[13] += (-vEye.x*mPtr[1])+(-vEye.y*mPtr[5])+(-vEye.z*mPtr[9]);
    mPtr[14] += (-vEye.x*mPtr[2])+(-vEye.y*mPtr[6])+(-vEye.z*mPtr[10]);
    mPtr[15] += (-vEye.x*mPtr[3])+(-vEye.y*mPtr[7])+(-vEye.z*mPtr[11]);

    return result;
}



RenderWorldLocal::RenderWorldLocal()
{

}

RenderWorldLocal::~RenderWorldLocal()
{

}

RenderModel* RenderWorldLocal::ImportModelFromFile( const char* fileName )
{
    RenderModel*    model = modelManager->GetModelFromFile( fileName );
    return model;
}

RenderModel* RenderWorldLocal::GenerateStaticModel( genType_t type )
{
    RenderModel*    model = modelManager->GenerateStaticModel( type );

    return model;
}

const char* RenderWorldLocal::GetMapName() const
{
    return mapName;
}

void RenderWorldLocal::AddEntity( renderEntity_t& ent )
{
    ent.entityId = entityDefs.size();

    entityDefs.append( ent );
}

void RenderWorldLocal::AddLight( renderLight_t& light )
{
    light.lightId = lightDefs.size();

    if( light.lightMaterial == NULL ) {
        light.lightMaterial = materialManager->GetDefaultMaterial();
    }
    lightDefs.append( light );
}

void RenderWorldLocal::SetRenderView( const renderView_t& view )
{
    mainView = view;
}

renderView_t RenderWorldLocal::GetMainView() const
{
    return mainView;
}

void RenderWorldLocal::UpdateScene()
{
    glViewport( mainView.x, mainView.y, mainView.width, mainView.height );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    R_GLPerspectivef( mainView.fovy, mainView.aspect, mainView.nearPlane, mainView.farPlane );

    inf::Vec3   target(0,0,0);
    inf::Vec3   headUp(0,1,0);
    mainView.world = R_BuildViewMatrixLookAt( mainView.viewOrg, target, headUp, true );
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf( mainView.world.to_float_ptr() );

    renderLight_t*  light = NULL;
    int             glLightSwitch;
    for( int i=0; i<lightDefs.size(); ++i ) {
        light = &lightDefs[i];

        glLightSwitch = GL_LIGHT0 + light->lightId;

        glLightfv( glLightSwitch, GL_POSITION, light->position.to_float_ptr() );

        glLightfv( glLightSwitch, GL_AMBIENT, light->ambient.to_float_ptr() );
        glLightfv( glLightSwitch, GL_DIFFUSE, light->diffuse.to_float_ptr() );
        glLightfv( glLightSwitch, GL_SPECULAR, light->specular.to_float_ptr() );

        glEnable( glLightSwitch );
    }
}

void RenderWorldLocal::RenderScene()
{
    renderEntity_t* entity = NULL;
    RenderModel*    model = NULL;
    srfTriangles_t* surf = NULL;

    if( rs_lightMap.GetBool() ) {
        // setup lightmap for this world...
    }

    for( int i=0; i<entityDefs.size(); ++i ) {
        entity = &entityDefs[i];
        model = entityDefs[i].model;

        entity->world.identity();
        entity->world[3].set( entity->position.x, entity->position.y, entity->position.z, 1.0 );
        inf::Mat4 modelView = entity->world * mainView.world;

        glMatrixMode( GL_MODELVIEW );
        glLoadMatrixf( modelView.to_float_ptr() );

        const int nSurf = model->NumSurfaces();
        modelSurface_t* modelSurf = NULL;
        for( int iSurf=0; iSurf<nSurf; ++iSurf ) {
            modelSurf = model->GetSurface( iSurf );
            if( modelSurf->shader )
                modelSurf->shader->Apply();
            RenderModelStatic* staticModel = static_cast<RenderModelStatic*>( model );
            surf = modelSurf->geometry;

            glBindBuffer( GL_ARRAY_BUFFER, staticModel->glBufVtxIds[modelSurf->id] );
            glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, staticModel->glBufIdxIds[modelSurf->id] );

            const void* offsetStart = (void*)&(surf->verts[0].xyz);
            void* offset = (void*)0;
            glEnableClientState( GL_VERTEX_ARRAY );
            glVertexPointer(3, GL_FLOAT, sizeof(DrawVert), offset);

            offset = (void*)((int)&(surf->verts[0].st[0]) - (int)offsetStart);
            glEnableClientState( GL_TEXTURE_COORD_ARRAY );
            glTexCoordPointer(2, GL_FLOAT, sizeof(DrawVert), offset);

            // setup lightmap coord channel data...

            offset = (void*)((int)&(surf->verts[0].normal) - (int)offsetStart);
            glEnableClientState( GL_NORMAL_ARRAY );
            glNormalPointer(GL_FLOAT, sizeof(DrawVert), offset);

            offset = (void*)((int)&(surf->verts[0].color) - (int)offsetStart);
            glEnableClientState( GL_COLOR_ARRAY );
            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(DrawVert), offset);

            glDrawElements(GL_TRIANGLES, surf->numIndexes, GL_UNSIGNED_SHORT, 0);
        }
    }
}

renderView_t RenderWorldLocal::GetDefaultView() const
{
    static renderView_t defaultView;
    static RenderSystemGLES*    rs = static_cast<RenderSystemGLES*>( renderSystem );

    defaultView.width = rs->currentContext->GetWidth();
    defaultView.height = rs->currentContext->GetHeight();
    defaultView.x = 0;
    defaultView.y = 0;

    defaultView.aspect = (float)defaultView.width/defaultView.height;

    defaultView.fovy = 60;
    defaultView.nearPlane = 0.3f;
    defaultView.farPlane = 1200.0f;

    return defaultView;
}

